package com.everbest.flowable.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.ProcessInstanceHistoryLog;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.everbest.flowable.ResultBean;
import com.everbest.flowable.ResultDesc;
import com.everbest.flowable.SoftUtil;
import com.everbest.flowable.WorkFlowUtil;
import com.everbest.flowable.mybatisbean.FinishTaskEntity;
import com.everbest.flowable.mybatisbean.WorkFlowRecordEntity;
import com.everbest.flowable.mybatisdao.ProcessDefinitionInit;

@Component
public class WorkFlowUtilImpl implements WorkFlowUtil {

    @Autowired
    private TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ProcessDefinitionInit processDefinitionInitSql;

    @Override
    public ResultBean<Deployment> deploy(String bpmnFileName, String processId, String processName) {
        ResultBean<Deployment> resultBean = new ResultBean<>();
        // ProcessDefinitionBean processDefinitionBean = new ProcessDefinitionBean(processId,processName);
        // processDefinitionInitSql.insertIgnore(processDefinitionBean);
        Deployment deployment = repositoryService.createDeployment().name(processName).key(processId)
            .addClasspathResource("processes/" + bpmnFileName).deploy();
        resultBean.set(deployment).set(ResultDesc.PROCESSDEFINITION_DEPLOYED);
        return resultBean;
    }

    @Override
    public ResultBean<ProcessInstance> start(String processDefinitionKey) {
        return start(processDefinitionKey, null, null);
    }

    @Override
    public ResultBean<ProcessInstance> start(String processDefinitionKey, String businessKey) {
        if (businessKey == null || businessKey.isEmpty()) {
            return new ResultBean<ProcessInstance>().set(ResultDesc.BUSINESSKEY_NULL);
        }
        return start(processDefinitionKey, businessKey, null);
    }

    @Override
    public ResultBean<ProcessInstance> start(String processDefinitionKey, String businessKey,
        Map<String, Object> variables) {
        ResultBean<ProcessInstance> resultBean = new ResultBean<>();
        if (processDefinitionKey == null || processDefinitionKey.isEmpty()) {
            return resultBean.set(ResultDesc.PROCESSDEFINITIONKEY_NULL);
        }
        return resultBean.set(runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables))
            .set(ResultDesc.PROCESSINSTANCE_STARTED);
    }

    @Override
    public ResultBean<List<Task>> selectAllTasks(String assignee) {
        ResultBean<List<Task>> resultBean = new ResultBean<>();
        if (assignee == null || assignee.isEmpty()) {
            return resultBean.set(ResultDesc.ASSIGNEE_NULL);
        }
        return resultBean
            .set(taskService.createTaskQuery().taskAssignee(assignee).orderByTaskCreateTime().desc().list()).success();
    }

    @Override
    public ResultBean<List<Task>> selectGroupTasks(String assignee, String orgId) {
        ResultBean<List<Task>> resultBean = new ResultBean<>();
        if (assignee == null || assignee.isEmpty()) {
            return resultBean.set(ResultDesc.ASSIGNEE_NULL);
        }
        if (orgId == null || orgId.isEmpty()) {
            return resultBean.set(ResultDesc.ORGID_NULL);
        }
        return resultBean.set(taskService.createTaskQuery().or().taskAssignee(assignee).taskCandidateGroup(orgId)
            .endOr().orderByTaskCreateTime().desc().list()).success();
    }

    @Override
    public ResultBean<List<Task>> selectGroupTasks(String assignee, String processDefinitionKey, String orgId) {
        ResultBean<List<Task>> resultBean = new ResultBean<>();
        boolean checkResult = selectAllTasksHelp(resultBean, assignee, processDefinitionKey);
        if (checkResult) {
            return resultBean;
        }
        if (orgId == null || orgId.isEmpty()) {
            return resultBean.set(ResultDesc.ORGID_NULL);
        }
        return resultBean
            .set(taskService.createTaskQuery().processDefinitionKey(processDefinitionKey).or().taskAssignee(assignee)
                .taskCandidateGroup(orgId).taskCandidateUser(assignee).endOr().orderByTaskCreateTime().desc().list())
            .success();
    }

    @Override
    public ResultBean<List<Task>> selectAllTasks(String assignee, int limit) {
        return selectAllTasks(assignee, 0, limit);
    }

    @Override
    public ResultBean<List<Task>> selectAllTasks(String assignee, int firstNum, int length) {
        ResultBean<List<Task>> resultBean = new ResultBean<>();
        int checkResult = checkEntering(assignee);
        if (checkResult == 0) {
            return resultBean.set(ResultDesc.ASSIGNEE_NULL);
        }
        return resultBean.set(taskService.createTaskQuery().taskAssignee(assignee).orderByTaskCreateTime().desc()
            .listPage(firstNum, length)).success();
    }

    @Override
    public ResultBean<List<Task>> selectAllTasks(String assignee, String processDefinitionKey) {
        ResultBean<List<Task>> resultBean = new ResultBean<>();
        boolean checkResult = selectAllTasksHelp(resultBean, assignee, processDefinitionKey);
        if (checkResult) {
            return resultBean;
        }
        return resultBean.set(taskService.createTaskQuery().taskAssignee(assignee)
            .processDefinitionKey(processDefinitionKey).orderByTaskCreateTime().desc().list()).success();
    }

    @Override
    public ResultBean<Integer> selectAllTasksCount(String processDefinitionKey, String taskDefinitionKey) {
        int size = taskService.createTaskQuery().processDefinitionKey(processDefinitionKey)
            .taskDefinitionKey(taskDefinitionKey).list().size();
        ResultBean<Integer> resultBean = new ResultBean<>();
        return resultBean.setBean(size).success();
    }

    @Override
    public ResultBean<Integer> selectAllTasksCount(String assignee, String processDefinitionKey,
        String taskDefinitionKey) {
        int size = taskService.createTaskQuery().taskAssignee(assignee).taskDefinitionKey(taskDefinitionKey)
            .processDefinitionKey(processDefinitionKey).list().size();
        ResultBean<Integer> resultBean = new ResultBean<>();
        return resultBean.setBean(size).success();
    }

    @Override
    public ResultBean<List<Task>> selectTasks(String assignee, String instanceId) {
        ResultBean<List<Task>> resultBean = new ResultBean<>();
        if (assignee == null || assignee.isEmpty()) {
            return resultBean.set(ResultDesc.ASSIGNEE_NULL);
        }
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        return resultBean.set(taskService.createTaskQuery().taskAssignee(assignee).processInstanceId(instanceId)
            .orderByTaskCreateTime().desc().list()).success();
    }

    @Override
    public ResultBean<Task> selectTask(String assignee, String taskId) {
        ResultBean<Task> resultBean = new ResultBean<>();
        if (assignee == null || assignee.isEmpty()) {
            return resultBean.set(ResultDesc.ASSIGNEE_NULL);
        }
        return resultBean.set(taskService.createTaskQuery().taskAssignee(assignee).taskId(taskId).singleResult())
            .success();
    }

    @Override
    public ResultBean<String> selectNextApprovalId(String instanceId) {
        ResultBean<String> resultBean = new ResultBean<>();
        ProcessInstance processInstance =
            runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        List<Task> list = taskService.createTaskQuery().processInstanceId(instanceId).list();
        if (list == null || list.size() < 1) {
            return resultBean.error();
        }
        String assignee = list.get(0).getAssignee();
        return resultBean.setBean(assignee).success();
    }

    @Override
    public ResultBean<Task> selectTask(String assignee, String instanceId, String taskDefinitionKey) {
        ResultBean<Task> resultBean = new ResultBean<>();
        if (assignee == null || assignee.isEmpty()) {
            return resultBean.set(ResultDesc.ASSIGNEE_NULL);
        }
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        return resultBean.set(taskService.createTaskQuery().taskAssignee(assignee).processInstanceId(instanceId)
            .taskDefinitionKey(taskDefinitionKey).singleResult()).success();
    }

    @Override
    public ResultBean<Task> selectTask(String taskId) {
        ResultBean<Task> resultBean = new ResultBean<>();
        if (taskId == null || taskId.isEmpty()) {
            return resultBean.set(ResultDesc.TASKID_NULL);
        }
        return resultBean.set(taskService.createTaskQuery().taskId(taskId).singleResult()).success();
    }

    @Override
    public ResultBean<Task> selectCurrentTask(String instanceId) {
        ResultBean<Task> resultBean = new ResultBean<>();
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        List<Task> list = taskService.createTaskQuery().processInstanceId(instanceId).list();
        if (list.size() < 1) {
            return resultBean.error();
        }
        Task task = list.get(0);
        return resultBean.set(task).success();
    }

    @Override
    public ResultBean<String> selectTaskId(Long userId, String instanceId, String taskDefinitionKey) {
        // 根据条件查询任务
        Task task = taskService.createTaskQuery().taskDefinitionKey(taskDefinitionKey).processInstanceId(instanceId)
            .taskAssignee(userId + "").singleResult();
        // 返回结果
        return (task == null) ? new ResultBean<String>().error() : new ResultBean<String>().set(task.getId()).success();
    }

    @Override
    public ResultBean<HistoricTaskInstance> selectHistoryTask(String taskId) {
        ResultBean<HistoricTaskInstance> resultBean = new ResultBean<>();
        if (taskId == null || taskId.isEmpty()) {
            return resultBean.set(ResultDesc.TASKID_NULL);
        }
        return resultBean.set(historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult()).success();
    }

    @Override
    public ResultBean pickingTask(String taskId, String assignee) {
        ResultBean resultBean = new ResultBean();
        if (assignee == null || assignee.isEmpty()) {
            return resultBean.set(ResultDesc.ASSIGNEE_NULL);
        }
        if (taskId == null || taskId.isEmpty()) {
            return resultBean.set(ResultDesc.TASKID_NULL);
        }
        Task task = taskService.createTaskQuery().taskId(taskId).taskUnassigned().singleResult();
        if (task == null) {
            return resultBean.set(ResultDesc.TASK_CANNOTPICK);
        }
        taskService.claim(taskId, assignee);
        return resultBean.set(ResultDesc.TASK_PICKSUCCESS);
    }

    @Override
    public ResultBean<String> selectBusinessKey(String taskId) {
        ResultBean<Task> taskResultBean = selectTask(taskId);
        ResultBean<String> stringResultBean = new ResultBean<>();
        Task task = taskResultBean.getBean();
        if (task == null) {
            return stringResultBean.set(taskResultBean.getCode());
        }
        return selectBusinessKey(task);
    }

    @Override
    public ResultBean<String> selectBusinessKey(Task task) {
        ResultBean<String> resultBean = new ResultBean<>();
        if (task == null) {
            return resultBean.set(ResultDesc.TASK_NULL);
        }
        String instanceId = task.getProcessInstanceId();
        ProcessInstance processInstance =
            runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        return resultBean.set(processInstance.getBusinessKey()).success();
    }

    @Override
    public ResultBean<String> selectBusinessKeyByInstanceId(String instanceId) {
        ResultBean<String> resultBean = new ResultBean<>();
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        ProcessInstanceHistoryLog processInstanceHistoryLog =
            historyService.createProcessInstanceHistoryLogQuery(instanceId).singleResult();
        return resultBean.set(processInstanceHistoryLog.getBusinessKey()).success();
    }

    @Override
    public ResultBean<Set<String>> selectBusinessKeys(List<Task> task) {
        ResultBean<Set<String>> resultBean = new ResultBean<>();
        Set<String> set = new HashSet<>();
        for (Task t : task) {
            String instanceId = t.getProcessInstanceId();
            ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
            set.add(processInstance.getBusinessKey());
        }
        return resultBean.set(set).success();
    }

    @Override
    public ResultBean<List<HistoricActivityInstance>> selectHistory(String instanceId) {
        ResultBean<List<HistoricActivityInstance>> resultBean = new ResultBean<>();
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        return resultBean.set(
            historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId).activityType("userTask")
                .orderByProcessInstanceId().orderByHistoricActivityInstanceStartTime().asc().list())
            .success();
    }

    @Override
    public ResultBean<List<WorkFlowRecordEntity>> selectHistoryRecord(String instanceId) {
        ResultBean<List<WorkFlowRecordEntity>> resultBean = new ResultBean<>();
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        List<WorkFlowRecordEntity> entityList = new ArrayList<>();
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(instanceId)
            .orderByTaskCreateTime().asc().list();
        list.forEach(historyTask -> {
            String id = historyTask.getId();
            String name = historyTask.getName();
            WorkFlowRecordEntity workFlowRecordEntity = new WorkFlowRecordEntity();
            workFlowRecordEntity.setName(name);
            List<Comment> taskResults = taskService.getTaskComments(id, "result");
            workFlowRecordEntity.setResultMes(taskResults.size() > 0 ? taskResults.get(0).getFullMessage() : "");
            List<Comment> taskComments = taskService.getTaskComments(id);
            workFlowRecordEntity.setComment(taskComments.size() > 0 ? taskComments.get(0).getFullMessage() : "");
            String userId = historyTask.getAssignee();
            workFlowRecordEntity.setUserId(Long.valueOf(userId));
            workFlowRecordEntity.setFinishDate(SoftUtil.dateToString(historyTask.getEndTime()));
            entityList.add(workFlowRecordEntity);
        });
        return resultBean.set(entityList).success();
    }

    @Override
    public ResultBean<List<HistoricActivityInstance>> selectFlowHistory(String instanceId) {
        ResultBean<List<HistoricActivityInstance>> resultBean = new ResultBean<>();
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        return resultBean.set(historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId)
            .orderByHistoricActivityInstanceStartTime().asc().list()).success();
    }

    @Override
    public ResultBean<Boolean> isFinish(String instanceId) {
        ResultBean<Boolean> resultBean = new ResultBean<>();
        if (instanceId == null || instanceId.isEmpty()) {
            return resultBean.set(ResultDesc.INSTANCEID_NULL);
        }
        ProcessInstance processInstance =
            runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        boolean isFinish = processInstance == null || processInstance.isEnded();
        return resultBean.set(isFinish).success();
    }

    @Override
    public ResultBean finishTask(String assignee, String taskId) {
        return finishTask(assignee, taskId, null);
    }

    @Override
    public ResultBean finishTask(String assignee, String taskId, Map<String, Object> variables) {
        ResultBean resultBean = new ResultBean();
        Task task = selectTask(assignee, taskId).getBean();
        if (task == null) {
            return resultBean.set(ResultDesc.TASK_NONE);
        }
        taskService.complete(taskId, variables);
        return resultBean.set(ResultDesc.TASK_FINISH);
    }

    @Override
    public ResultBean finishTaskWithComment(FinishTaskEntity finishTaskEntity) {
        ResultBean resultBean = new ResultBean<>();
        String taskId = finishTaskEntity.getTaskId();
        ResultBean<Task> taskResultBean = selectTask(taskId);
        if (taskResultBean.isError()) {
            return resultBean.set(ResultDesc.TASK_NONE);
        }
        Task bean = taskResultBean.getBean();
        String instanceId = bean.getProcessInstanceId();
        taskService.addComment(taskId, instanceId, "result", finishTaskEntity.getResultMes());
        String commentMes = finishTaskEntity.getCommentMes();
        if (commentMes != null && !commentMes.isEmpty()) {
            addComment(taskId, instanceId, finishTaskEntity.getCommentMes());
        }
        finishTask(finishTaskEntity.getAssignee(), taskId, finishTaskEntity.getVariables());
        return resultBean.set(ResultDesc.TASK_FINISH);
    }

    @Override
    public ResultBean addComment(String taskId, String instanceId, String... message) {
        ResultBean resultBean = new ResultBean();
        for (String m : message) {
            taskService.addComment(taskId, instanceId, m);
        }
        return resultBean.success();
    }

    @Override
    public ResultBean finishTaskTidyGroup(String assignee, String taskId, Map<String, Object> variables) {
        // 完成指定的任务
        ResultBean resultBean = finishTask(assignee, taskId, variables);
        if (resultBean.getCode() != ResultDesc.TASK_FINISH) {
            return resultBean;
        }
        tidyGroup(taskId);
        return resultBean;
    }

    private void tidyGroup(String taskId) {
        // 获取流程实例的id
        ResultBean<HistoricTaskInstance> historicTaskInstanceResultBean = selectHistoryTask(taskId);
        String processInstanceId = historicTaskInstanceResultBean.getBean().getProcessInstanceId();
        // 判断当前流程是否完成
        ResultBean<Boolean> finish = isFinish(processInstanceId);
        if (finish.getBean()) {
            return;
        }
        // 查询判断当前流程实例的 当前任务是否没有 assignee
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).taskUnassigned().list();
        // 如果没有需要查询实例的历史 对当请执行的任务 赋予 assignee
        for (Task t : list) {
            String unassignedTaskId = t.getId();
            String taskDefinitionKey = t.getTaskDefinitionKey();
            ResultBean<String> assigneeFromHistory = selectAssigneeFromHistory(processInstanceId, taskDefinitionKey);
            if (assigneeFromHistory.isError()) {
                continue;
            }
            String historyAssignee = assigneeFromHistory.getBean();
            ResultBean resultBean = pickingTask(unassignedTaskId, historyAssignee);
            if (resultBean.getCode() != ResultDesc.TASK_PICKSUCCESS) {
                return;
            }
        }
    }

    @Override
    public ResultBean<Object> getVariable(String instanceId, String key) {
        Object variable = runtimeService.getVariable(instanceId, key);
        ResultBean<Object> resultBean = new ResultBean<>();
        return resultBean.set(variable).success();
    }

    @Override
    public ResultBean<String> selectAssigneeFromHistory(String processId, String activityId) {
        List<HistoricActivityInstance> historicActivityInstanceList =
            historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).activityId(activityId)
                .finished().list();
        if (historicActivityInstanceList.size() < 1) {
            return new ResultBean<String>().error();
        }
        HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get(0);
        return new ResultBean<String>().set(historicActivityInstance.getAssignee()).success();
    }

    @Override
    public ResultBean deleteInstance(String instanceId, String deleteReason) {
        ResultBean resultBean = new ResultBean();
        ProcessInstance processInstance =
            runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).active().singleResult();
        if (processInstance == null) {
            return resultBean.set(ResultDesc.PROCESSINSTANCE_NONE);
        }
        runtimeService.deleteProcessInstance(instanceId, deleteReason);
        return resultBean.set(ResultDesc.INSTANCE_DELETED);
    }

    @Override
    public ResultBean deleteDefinition(String definitionKey) {
        return deleteDefinition(definitionKey, false);
    }

    @Override
    public ResultBean deleteDefinition(String definitionKey, boolean cascade) {
        ResultBean resultBean = new ResultBean();
        List<ProcessDefinition> processDefinitionInitList =
            repositoryService.createProcessDefinitionQuery().processDefinitionKey(definitionKey).list();
        if (processDefinitionInitList.size() == 0) {
            return resultBean.set(ResultDesc.PROCESSDEFINITION_NONE);
        }
        processDefinitionInitList.forEach(pd -> repositoryService.deleteDeployment(pd.getDeploymentId(), cascade));
        return resultBean.set(ResultDesc.PROCESSDEFINITION_DELETED);
    }

    /**
     * 查询多个任务的入参非空判断方法
     * 
     * @param resultBean
     *            结果对象
     * @param assignee
     *            用户id
     * @param processDefinitionKey
     *            流程定义id
     * @return 是否有空字段
     */
    private boolean selectAllTasksHelp(ResultBean<List<Task>> resultBean, String assignee,
        String processDefinitionKey) {
        int checkResult = checkEntering(assignee, processDefinitionKey);
        if (checkResult == 0) {
            resultBean.set(ResultDesc.ASSIGNEE_NULL);
        } else if (checkResult == 1) {
            resultBean.set(ResultDesc.PROCESSDEFINITIONKEY_NULL);
        } else {
            return false;
        }
        return true;
    }

    /**
     * 一个简单的判空方法
     * 
     * @param args
     *            参数
     * @return 参数为空时的索引值
     */
    private int checkEntering(String... args) {
        for (int i = 0; i < args.length; i++) {
            if (args[i] == null || args[i].isEmpty()) {
                return i;
            }
        }
        return -1;
    }

}
